/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package main.ContextFree;

import java.io.Serializable;
import java.util.Map.Entry;
import java.util.*;
import java.util.regex.Pattern;
import main.CharacterTypes.Character1;
import main.Trie.AbstractTrie;

/**
 *
 * @author Isaac Osesina
 */
public class DynamicRegexGenerator implements Serializable, Cloneable{
    private double threshold;

    public DynamicRegexGenerator() {
    }

    public DynamicRegexGenerator(double threshold) {
        this.threshold = threshold;
    }
    
    public Pattern defineRegex(Collection<String> regexSource) {
        return defineRegex(regexSource, threshold);
    }
    
    
    public Pattern defineRegex(Collection<String> regexSource, double threshold){
        
        if(regexSource == null)
            return null;
      
        int minLength = Integer.MAX_VALUE;
        List<Set<Character>> transpose = new ArrayList<Set<Character>>();
        for (Iterator<String> it = regexSource.iterator(); it.hasNext();) {
            String s = it.next();

            if(s==null)
                continue;

            if (s.length()<minLength)
                minLength = s.length();
            for (int i = 0; i < s.length(); i++) {
                char c = s.charAt(i);
                if (transpose.size() == i) {
                    transpose.add(new HashSet<Character>());
                }
                transpose.get(i).add(c);
            }
        }
        
        return defineRegex(transpose, minLength, threshold);
    }


    private Pattern defineRegex(List<Set<Character>> charsLevel, int minLen, double threshold) {
        if(charsLevel != null) {
            List<String> regexGroups = new ArrayList<String>();
            for(Set<Character> level: charsLevel) {
                regexGroups.add( createRegexForCharacterSet(level, threshold) );
            }
            String regex = optimizeRegexCollection(regexGroups, minLen);
            Pattern p = Pattern.compile(regex);
            return p;
        }
        return null;
    }


    
    /*
     * Gets replaces consecutive identical strings with a single string and 
     * appends the number of occurence i.e. {min # of co-occurence, max # of co-occurence}
     */
    public String optimizeRegexCollection(List<String> regexGroups, int minLen) {
        String regex = "";
        
        if(regexGroups == null)
            return regex;
        
        Stack<String> regexStack = new Stack<String>();
        List<String> regexgroup_ = new ArrayList(regexGroups);
        Collections.reverse(regexgroup_);
        for(String level: regexgroup_) {
            regexStack.push(level);
        }
        int level = 0;
        while(regexStack.size()>0) {
            level++;
            String pop = regexStack.pop();
            int count = 1;
            while( regexStack.size()>0 && regexStack.peek().compareTo(pop)==0 ) {
                level++;
                regexStack.pop();
                count++;
            }
            if(level>minLen) {
                String start = "{0,";
                if(count>1) {
                    regex += pop + start + count + "}";
                }
                else {
                    regex += pop + start + "1}";
                }
            }
            else {
                if(count>1) {
                    regex += pop + "{" + count + "}";
                }
                else {
                    regex += pop;
                }
            }
        }
        return regex;
    }

    private String createRegexForCharacterSet(Set<Character> chars, double threshold) {
        TreeMap<Integer, Set<Character>> charTypesGroups = new TreeMap<Integer, Set<Character>>();
        for(char c:chars) {
            if(Character1.isDefined(c)) {
                int type = Character1.getType(c);
                if(charTypesGroups.containsKey(type)) {
                    charTypesGroups.get(type).add(c);
                }
                else {
                    Set<Character> newList = new HashSet<Character>();
                    newList.add(c);
                    charTypesGroups.put(type, newList);
                }
            }
        }
        return generateLevelRegex(charTypesGroups, threshold);
    }

    private String generateLevelRegex(TreeMap<Integer, Set<Character>> charTypesGroups, double threshold) {
        Set<String> levelRegexGroups = new HashSet<String>();
        for(int type:charTypesGroups.keySet()) {
            levelRegexGroups.add( createLevelPartRegex(type, charTypesGroups.get(type), threshold) );
        }
        String str = createRegexGroup(levelRegexGroups);
        return "[" + Pattern.compile(str).pattern()  + "]";
    }

    private String createLevelPartRegex(int type, Set<Character> charsOfSameType, double threshold) {
        Set<String> typeRegexGroups = new HashSet<String>();
        threshold = threshold==0 || threshold>1 ? this.threshold:threshold;
        List<Map.Entry<List<Character>, List<Character>>> allCharactersOfType_Count = new ArrayList<Map.Entry<List<Character>, List<Character>>>();
        for(char c:charsOfSameType) {
            List<Character> charactersWithType = Character1.getCharactersWithType(type, c);
            if(charactersWithType == null) {
                typeRegexGroups.add(c+"");
            }
            else {
                boolean found = false;
                for(Map.Entry<List<Character>, List<Character>> charTypeList_charFound_Entry:allCharactersOfType_Count) {
                    if(charTypeList_charFound_Entry.getKey().containsAll(charactersWithType)) {
                        charTypeList_charFound_Entry.getValue().add(c);
                        found = true;
                        break;
                    }
                }
                if(!found) {
                    List<Character> chars = new ArrayList<Character>();
                    chars.add(c);
                    allCharactersOfType_Count.add( new AbstractTrie.Entry<List<Character>, List<Character>>(charactersWithType, chars));
                }
            }
        }
        for(Entry<List<Character>, List<Character>> entry : allCharactersOfType_Count) {
            int groupSize = entry.getKey().size();
            int foundCharCount = entry.getValue().size();
            if( ((double)foundCharCount/(double)groupSize) < threshold) {
                //too few chars in group found so use only found characters
                typeRegexGroups.add( characterArrayToString(entry.getValue()) );
            }
            else {
                //enough chars in group were found to give suffient grounds for using entire group
                typeRegexGroups.add( characterArrayToString(entry.getKey()) );
            }
        }
        return createRegexGroup(typeRegexGroups);
    }

    private String createRegexGroup(Set<String> typeRegexGroups) {
        String str = "";
        for(String s:typeRegexGroups) {
            str += s;
        }
        return str;
    }

    public String characterArrayToString(List<Character> chars) {
        char[] escape = {'b','t', 'n', 'f', 'r', '"', '\'' };
        String s = "";
        for(char c:chars) {
            if( (c=='\\') ^ (c=='[') ^ (c==']') ^ (c=='-') ^ (c=='+')
                 ^ (c=='*') ^ (c=='(') ^ (c==')') ^ (c=='^') ^ (c=='.')
                 ^ (c=='?') ^ (c=='|')  ^ (c=='{') ^ (c=='}') ^ (c=='&')  ) {
                s += "\\" + c;
            }
            else {
                s += c;
            }
        }
        return s;
    }

    public double getThreshold() {
        return threshold;
    }

    public void setThreshold(double threshold) {
        this.threshold = threshold;
    }
    
}
